﻿using Autofac;
using Autofac.Core;
using Autofac.Core.Resolving.Pipeline;
using Autofac.Features.AttributeFilters;
using AutoRegistDependency.Component;
using AutoRegistDependency.Utils;
using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace AutoRegistDependency.Attributes
{
    /// <summary>
    /// 自动装配特性，可以标记在属性、字段以及方法上。可以根据特性的属性筛选不同的实现类。
	/// Automatic assembly features can be marked on attributes, fields, and methods.
	/// You can filter different implementation classes based on the properties of the feature.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public sealed class AutowiredAttribute : ParameterFilterAttribute
    {
        /// <summary>
        /// 服务的name。
        /// Service name.
        /// </summary>
        public string Name { get; }
        /// <summary>
        /// 服务的key
        /// Service key.
        /// </summary>
        public object Key { get; }
        public bool Required { get; set; }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="name">需要获取的服务名(根据参数的类型和name去取)。The service name that needs to be obtained (based on the type and name of the parameter)</param>
        /// <param name="required">注入时未获取到实例是否抛出异常，默认抛出。When injecting, no instance was obtained to determine if an exception was thrown. It is thrown by default.</param>
        public AutowiredAttribute(string name = "",bool required=true)
        {
            Name = name;
            Required = required;
        }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="key">需要获取的服务的key名。The key name of the service that needs to be obtained</param>
        /// <param name="reqired">注入时未获取到实例是否抛出异常，默认抛出。When injecting, no instance was obtained to determine if an exception was thrown. It is thrown by default.</param>
        public AutowiredAttribute(object key, bool reqired = true)
        {
            Key = key;
            Required = reqired;
        }

        public override object ResolveParameter(ParameterInfo parameter, IComponentContext context)
        {
            if (parameter == null) return null;
            if (string.IsNullOrWhiteSpace(Name) && Key == null)
            {
                return context.Resolve(parameter.ParameterType);
            }
            else
            {
                if (Key != null)
                {
                    return context.ResolveKeyed(Name, parameter.ParameterType);
                }
                else
                {
                    return context.ResolveNamed(Name, parameter.ParameterType);
                }

            }
        }

        public override bool CanResolveParameter(ParameterInfo parameter, IComponentContext context)
        {
            return true;
        }
        /// <summary>
        /// 执行属性注入
        /// </summary>
        /// <param name="instance">实例</param>
        /// <param name="property">属性</param>
        /// <param name="resolveParameter">解析参数(循环依赖会用到)</param>
        /// <param name="context">Ioc上下文</param>
        internal void ExecutePropertyAutowiredInject(object instance, PropertyInfo property, ResolveParameter resolveParameter, ResolveRequestContext context)
        {
            var instanceInfo = GetInstanceRealInfo(instance);
            //AutowiredAttribute autowired = property.GetCustomAttribute<AutowiredAttribute>();
            object serviceInstance = GetServiceInstance(property.PropertyType, resolveParameter, context);
            if (serviceInstance != null)
            {
                AutoFacHelper.SetValue(instanceInfo.realInstance, instanceInfo.realType, property, serviceInstance);
            }
            else
            {
                if (Required)
                {
                    throw new ArgumentNullException($"{instance.GetType()} service error! Because property: ${property.Name} is null!");
                }
            }
        }
        /// <summary>
        /// 执行字段注入
        /// </summary>
        /// <param name="instance">实例</param>
        /// <param name="field">字段</param>
        /// <param name="resolveParameter">解析参数(循环依赖会用到)</param>
        /// <param name="context">Ioc上下文</param>
        internal void ExecuteFieldAutowiredInject(object instance, FieldInfo field, ResolveParameter resolveParameter, ResolveRequestContext context)
        {
            var instanceInfo = GetInstanceRealInfo(instance);
            //AutowiredAttribute autowired = field.GetCustomAttribute<AutowiredAttribute>();
            object serviceInstance = GetServiceInstance(field.FieldType, resolveParameter, context);
            if (serviceInstance != null)
            {
                AutoFacHelper.SetValue(instanceInfo.realInstance, instanceInfo.realType, field, serviceInstance);
            }
            else
            {
                if (Required)
                {
                    throw new ArgumentException($"{instance.GetType()} service error! Because field: ${field.Name} is null!");
                }
            }
        }
        /// <summary>
        /// 执行方法注入
        /// </summary>
        /// <param name="instance">实例</param>
        /// <param name="method">注入的方法</param>
        /// <param name="context">Ioc上下文</param>
        internal void ExecuteMethodAutowiredInject(object instance, MethodInfo method, ResolveRequestContext context)
        {
            _ = AutoFacHelper.InvokeMethod(instance, method, context, context.Parameters);
        }

        /// <summary>
        /// 获取真实的实例
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        internal (object realInstance, Type realType) GetInstanceRealInfo(object instance)
        {
            object realInstance = instance;
            Type realType = instance.GetType();
            if (ProxyUtil.IsProxy(instance))
            {
                realInstance = ProxyUtil.GetUnproxiedInstance(instance);
                realType = ProxyUtil.GetUnproxiedType(instance);
            }
            return (realInstance, realType);
        }
        /// <summary>
        /// 获取解析服务所用的key
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal Service GetResolveService(Type type)
        {
            Service service = null;
            if (!string.IsNullOrEmpty(Name))
            {
                service = new KeyedService(Name, type);
            }
            else if (Key != null)
            {
                service = new KeyedService(Key, type);
            }
            else
            {
                service = new TypedService(type);
            }
            return service;
        }
        /// <summary>
        /// 获取解析对象实例
        /// </summary>
        /// <param name="type">装配类型</param>
        /// <param name="resolveParameter">解析参数</param>
        /// <param name="context">解析上下文</param>
        /// <returns></returns>
        internal object GetServiceInstance(Type type, ResolveParameter resolveParameter, IComponentContext context)
        {
            Service service = GetResolveService(type);
            if (resolveParameter.TryGetCache(service, out object serviceInstance))
            {
            }
            else if (context.TryResolveService(service, new Parameter[] { resolveParameter }, out serviceInstance))
            {

            }
            return serviceInstance;
        }

    }
}
